package cn.nexttec.noteplayer.common;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

public class VideoDecoder {
    private static final String TAG = "VideoDecoder";

    private MediaExtractor mediaExtractor;
    private MediaMuxer mediaMuxer;

    /**
     * 剪辑视频（通过 File 传入源文件）
     */
    public boolean decodeVideo(File srcFile, String destFile, long clipPointUs, long clipDurationUs) {
        return decodeInternal(srcFile.getAbsolutePath(), destFile, clipPointUs, clipDurationUs);
    }

    /**
     * 剪辑视频（通过路径传入源文件）
     */
    public boolean decodeVideo(String srcPath, String destFile, long clipPointUs, long clipDurationUs) {
        return decodeInternal(srcPath, destFile, clipPointUs, clipDurationUs);
    }

    /**
     * 核心剪辑逻辑
     */
    private boolean decodeInternal(String srcPath, String destFile, long clipPointUs, long clipDurationUs) {
        try {
            mediaExtractor = new MediaExtractor();
            mediaExtractor.setDataSource(srcPath);
        } catch (IOException e) {
            Log.e(TAG, "Extractor setDataSource failed: " + e.getMessage());
            return false;
        }

        try {
            mediaMuxer = new MediaMuxer(destFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException e) {
            Log.e(TAG, "Create MediaMuxer failed: " + e.getMessage());
            return false;
        }

        return extractTracksAndWrite(clipPointUs, clipDurationUs);
    }

    /**
     * 提取并写入视频/音频轨道
     */
    private boolean extractTracksAndWrite(long clipPointUs, long clipDurationUs) {
        int videoTrackIndex = -1;
        int audioTrackIndex = -1;
        int outputVideoTrackIndex = -1;
        int outputAudioTrackIndex = -1;
        long videoDurationUs = 0;
        long audioDurationUs = 0;
        int maxInputSize = 1 * 1024 * 1024; // 1MB 默认缓冲区

        // 遍历轨道
        for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            Log.d(TAG, "Track " + i + " MIME: " + mime);

            if (mime.equals("video/av01")) {
                Log.e(TAG, "AV1 视频不被 MediaMuxer 支持，需转码后处理");
                return false;
            }

            if (mime.startsWith("video/") && videoTrackIndex == -1) {
                videoTrackIndex = i;
                videoDurationUs = format.containsKey(MediaFormat.KEY_DURATION) ? format.getLong(MediaFormat.KEY_DURATION) : 0;
                if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                    maxInputSize = Math.max(maxInputSize, format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE));
                }
                outputVideoTrackIndex = mediaMuxer.addTrack(format);
            } else if (mime.startsWith("audio/") && audioTrackIndex == -1) {
                audioTrackIndex = i;
                audioDurationUs = format.containsKey(MediaFormat.KEY_DURATION) ? format.getLong(MediaFormat.KEY_DURATION) : 0;
                if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                    maxInputSize = Math.max(maxInputSize, format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE));
                }
                outputAudioTrackIndex = mediaMuxer.addTrack(format);
            }
        }

        long durationUs = Math.max(videoDurationUs, audioDurationUs);
        if (clipPointUs >= durationUs) {
            Log.e(TAG, "Clip point exceeds duration!");
            return false;
        }
        if (clipDurationUs != 0 && (clipPointUs + clipDurationUs) >= durationUs) {
            Log.e(TAG, "Clip duration exceeds end!");
            return false;
        }

        ByteBuffer inputBuffer = ByteBuffer.allocate(maxInputSize);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

        try {
            mediaMuxer.start();

            // =========== 视频轨 ===========
            if (videoTrackIndex >= 0) {
                mediaExtractor.selectTrack(videoTrackIndex);
                mediaExtractor.seekTo(clipPointUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);

                long videoSampleTime = getAverageSampleTimeUs(videoTrackIndex, inputBuffer);
                bufferInfo.presentationTimeUs = 0;

                while (true) {
                    int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
                    if (sampleSize < 0) break;

                    long sampleTime = mediaExtractor.getSampleTime();
                    if (clipDurationUs != 0 && sampleTime > (clipPointUs + clipDurationUs)) break;

                    bufferInfo.offset = 0;
                    bufferInfo.size = sampleSize;
                    bufferInfo.presentationTimeUs += videoSampleTime;

                    // ✅ 转换 SAMPLE_FLAG_SYNC 到 BUFFER_FLAG_SYNC_FRAME
                    int sampleFlags = mediaExtractor.getSampleFlags();
                    bufferInfo.flags = 0;
                    if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
                        bufferInfo.flags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
                    }

                    mediaMuxer.writeSampleData(outputVideoTrackIndex, inputBuffer, bufferInfo);
                    mediaExtractor.advance();
                }
                mediaExtractor.unselectTrack(videoTrackIndex);
            }

            // =========== 音频轨 ===========
            if (audioTrackIndex >= 0) {
                mediaExtractor.selectTrack(audioTrackIndex);
                mediaExtractor.seekTo(clipPointUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);

                long audioSampleTime = getAverageSampleTimeUs(audioTrackIndex, inputBuffer);
                bufferInfo.presentationTimeUs = 0;

                while (true) {
                    int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
                    if (sampleSize < 0) break;

                    long sampleTime = mediaExtractor.getSampleTime();
                    if (clipDurationUs != 0 && sampleTime > (clipPointUs + clipDurationUs)) break;

                    bufferInfo.offset = 0;
                    bufferInfo.size = sampleSize;
                    bufferInfo.presentationTimeUs += audioSampleTime;

                    // ✅ 同样处理音频 flags
                    int sampleFlags = mediaExtractor.getSampleFlags();
                    bufferInfo.flags = 0;
                    if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
                        bufferInfo.flags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
                    }

                    mediaMuxer.writeSampleData(outputAudioTrackIndex, inputBuffer, bufferInfo);
                    mediaExtractor.advance();
                }
                mediaExtractor.unselectTrack(audioTrackIndex);
            }

        } catch (Exception e) {
            Log.e(TAG, "Muxing failed: " + e.getMessage());
            return false;
        } finally {
            try {
                mediaMuxer.stop();
            } catch (Exception e) {
                Log.e(TAG, "Muxer stop failed: " + e.getMessage());
            }
            try {
                mediaMuxer.release();
            } catch (Exception e) {
                Log.e(TAG, "Muxer release failed: " + e.getMessage());
            }
            mediaExtractor.release();
        }

        return true;
    }

    /**
     * 估算帧时间间隔（us）
     */
    private long getAverageSampleTimeUs(int trackIndex, ByteBuffer buffer) {
        long sampleTime = 33_333; // 默认30fps
        mediaExtractor.selectTrack(trackIndex);

        mediaExtractor.readSampleData(buffer, 0);
        if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
            mediaExtractor.advance();
        }
        mediaExtractor.readSampleData(buffer, 0);
        long first = mediaExtractor.getSampleTime();
        mediaExtractor.advance();
        mediaExtractor.readSampleData(buffer, 0);
        long second = mediaExtractor.getSampleTime();

        if (first > 0 && second > 0) {
            sampleTime = Math.abs(second - first);
        }

        mediaExtractor.unselectTrack(trackIndex);
        return sampleTime;
    }
}
